#include "qenvironment.h"
#include "point.h"

#include <QPainter>
#include <QResizeEvent>
#include <QStyleOption>
#include <QStylePainter>
#include <qRegShape.h>

using namespace physics;

QEnvironment::QEnvironment(QWidget *parent) :
    QWidget(parent), env(), _running(false), _last_update(monotonic_clock::now()), active(0)
{
    env.addBody(new Circle("337 315 0", "0 -50 0", 10, 10));
    env.addBody(new RegularShape(3, "100 60 0", "-30 30 0", 20, 20, .75));
    env.addBody(new RegularShape(4, "400 70 0", "-5 40 0", 30, 30, .5));
    env.addBody(new RegularShape(5, "250 80 0", "-25 -60 0", 40, 40, .6));
}

void QEnvironment::resizeEvent(QResizeEvent *e)
{
    QWidget::resizeEvent(e);
    const QSize &s = e->size();
    env.resize(s.width(), s.height());
}

void QEnvironment::timerEvent(QTimerEvent *)
{
    update();
}

int QEnvironment::startTimer(int milli)
{
    static int id, interval;
    if (!_running || interval != milli)
    {
        _running = true;
        _last_update = monotonic_clock::now();
        QWidget::killTimer(id);
        id = QWidget::startTimer(interval = milli);
    }
    return id;
}

void QEnvironment::killTimer(int id)
{
    _running = false;
    QWidget::killTimer(id);
}

void draw(QPainter &p, RigidBody *b)
{
    if (typeid(*b) == typeid(Circle))
    {
        Circle *c = static_cast<Circle*>(b);
        p.drawEllipse(QPointF(c->pX(), c->pY()), c->radius(), c->radius());
    }
    else
    {
        QPolygonF poly;
        IPolygon *s = dynamic_cast<IPolygon*>(b);
        for(const auto &v : s->vertices())
            poly.append(QPointF(v[0], v[1]));
        p.drawPolygon(poly);
    }

}

void QEnvironment::paintEvent(QPaintEvent *e)
{
//    static QPointF origin(width() / 2, height() / 2);

    QWidget::paintEvent(e);
    {
        QStyleOption opts;
        opts.init(this);
        QStylePainter painter(this);
        painter.drawPrimitive(QStyle::PE_Widget, opts);
    }
    QPainter painter(this);
    painter.setClipRect(e->rect());

    if (_running)
    {
        time_point<monotonic_clock> now = monotonic_clock::now();
        scalar time = duration_cast<milliseconds>(now - _last_update).count() / 1000.0;
        env.update(time);
        _last_update = now;
    }
    painter.setBrush(QBrush(Qt::green));
    for(RigidBody *b : env.bodies())
        draw(painter, b);
    if (active)
    {
        painter.setBrush(QBrush(Qt::yellow));
        draw(painter, active);
    }

/*    const auto &bods = env.bodies();
    auto end = bods.cend();
    std::unordered_set<RigidBody*> drawn;
    for(auto a = bods.cbegin(); a != end; ++a)
    {
        bool collide = false;
        ConvexShape *sA = dynamic_cast<ConvexShape*>(*a);
        for(auto b = std::next(a); b != end; ++b)
        {
            ConvexShape *sB = dynamic_cast<ConvexShape*>(*b);
            auto res = intersect(*sA, *sB);
            if((collide = res.first))
            {
                painter.setBrush(QBrush(Qt::red));
                if(!drawn.count(*a))
                {
                    draw(painter, *a);
                    drawn.insert(*a);
                }
                if(!drawn.count(*b))
                {
                    draw(painter, *b);
                    drawn.insert(*b);
                }
                break;
            }
        }
        if(!collide && !drawn.count(*a))
        {
            painter.setBrush(QBrush(Qt::green));
            draw(painter, *a);
            drawn.insert(*a);
        }
        painter.setBrush(QBrush(Qt::blue));
        painter.drawEllipse(origin, 3, 3);
    }
*/
}

void QEnvironment::mousePressEvent(QMouseEvent *e)
{
    e->accept();
    static Point pt;
    pt.position(e->x(), e->y());
    RigidBody *clicked = 0;
    for (const auto b : env.bodies())
    {
        const physics::ConvexShape *c = dynamic_cast<ConvexShape*>(b);
        vector3 blah;
        scalar blahs;
        if (intersect(pt, *c, blah, blahs, b->position() - pt.position()))
        {
            clicked = b;
            aMass = clicked->mass();
            clicked->mass(0);
            break;
        }
    }
    if (active != clicked)
    {
        active = clicked;
        emit activeChanged(active);
        update();
    }
}

void QEnvironment::mouseMoveEvent(QMouseEvent *e)
{
    if (active)
    {
        e->accept();
        active->position(e->x(), e->y());
        update();
    }
}

void QEnvironment::mouseReleaseEvent(QMouseEvent *)
{
    if (active)
        active->mass(aMass);
}

void QEnvironment::setActiveSize(int s)
{
    if (active)
    {
        if (typeid(*active) == typeid(Circle))
            static_cast<Circle*>(active)->radius(scalar(s));
        else
            static_cast<RegularShape*>(active)->size(scalar(s));
        update();
    }
}

void QEnvironment::setActiveMass(double m)
{
    if (active)
        active->mass(scalar(m));
}

void QEnvironment::setActiveElasticity(int e)
{
    if (active)
        active->elasticity(scalar(e / 100.0));
}

scalar QEnvironment::getActiveSize() const
{
    scalar size = 0;
    if (active)
    {
        if (typeid(*active) == typeid(Circle))
            size = static_cast<Circle*>(active)->radius();
        else
            size = static_cast<RegularShape*>(active)->size();
    }
    return size;
}

scalar QEnvironment::getActiveMass() const
{
    if (active) return aMass;
    else return 0;
}

scalar QEnvironment::getActiveElasticity() const
{
    if (active) return active->elasticity();
    else return -1;
}

void QEnvironment::setGravity(double g)
{
    env.gravity(scalar(g));
}

void QEnvironment::addBody(RigidBody *b)
{
//    RigidBody *body = new RigidBody(*b);
//    body->position(width() / 2, height() / 2);
//    env.addBody(body);
}
